using System;

namespace SmartSite.Frame
{
    public struct BitUnit : IEquatable<BitUnit>
    {
        readonly int Value;

        public BitUnit(int value)
        {
            Value = value;
        }

        public static implicit operator BitUnit(int a) => new BitUnit(a);
        public static implicit operator int(BitUnit a) => a.Value;
        public static BitUnit operator +(BitUnit a, BitUnit b) => new BitUnit(a.Value + b.Value);
        public static BitUnit operator -(BitUnit a, BitUnit b) => new BitUnit(a.Value - b.Value);
        public static BitUnit operator +(BitUnit a, int b) => new BitUnit(a.Value + b);
        public static BitUnit operator -(BitUnit a, int b) => new BitUnit(a.Value - b);
        public static BitUnit operator +(int a, BitUnit b) => new BitUnit(a + b.Value);
        public static BitUnit operator -(int a, BitUnit b) => new BitUnit(a - b.Value);


        public static BitUnit ToBitUnit(int a) => new BitUnit(a);
        public int FromBitUnit() => Value;
        public static BitUnit Add(BitUnit a, BitUnit b) => a + b;
        public static BitUnit Subtract(BitUnit a, BitUnit b) => a - b;
        public static BitUnit Add(BitUnit a, int b) => a + b;
        public static BitUnit Subtract(BitUnit a, int b) => a - b;
        public static BitUnit Add(int a, BitUnit b) => a + b;
        public static BitUnit Subtract(int a, BitUnit b) => a - b;

        public override int GetHashCode() => Value.GetHashCode();

        public override bool Equals(object obj) => (obj is BitUnit || obj is int) && Equals((BitUnit)obj);

        public bool Equals(BitUnit other) => Value == other.Value;
        public static bool operator ==(BitUnit a, BitUnit b) => a.Equals(b);
        public static bool operator !=(BitUnit a, BitUnit b) => !a.Equals(b);
        public static bool operator ==(BitUnit a, int b) => a.Equals(b);
        public static bool operator !=(BitUnit a, int b) => !a.Equals(b);
        public static bool operator ==(int a, BitUnit b) => b.Equals(a);
        public static bool operator !=(int a, BitUnit b) => !b.Equals(a);

        /// <summary>
        /// 计算掩码，即用当前个数的 1 值填充低位，其余高位填充 0
        /// </summary>
        /// <returns>Long 类型掩码</returns>
        public long LongMask() => ~(-1L << Value);

        /// <summary>
        /// 计算反码，即用当前个数的 0 值填充低位， 其余高位填充 1
        /// </summary>
        /// <returns>Long 类型反码</returns>
        public long LongInversedMask() => -1L << Value;

        /// <summary>
        /// 计算掩码，即用当前个数的 1 值填充低位，其余高位填充 0
        /// </summary>
        /// <returns>ULong 类型掩码</returns>
        public ulong ULongMask()
        {
            // 因 c# 对 64 位值左移 64 次等同于左移 0 次
            // 所以对 64 次左移要手工计算
            return Value == 64 ? ulong.MaxValue : ~(ulong.MaxValue << Value);
        }

        /// <summary>
        /// 计算反码，即用当前个数的 0 值填充低位， 其余高位填充 1
        /// </summary>
        /// <returns>ULong 类型反码</returns>
        public ulong ULongInversedMask()
        {
            return Value == 64 ? 0 : ulong.MaxValue << Value;
        }

        /// <summary>
        /// 计算掩码，即用当前个数的 1 值填充低位，其余高位填充 0
        /// </summary>
        /// <returns>int 类型掩码</returns>
        public int Mask() => ~(-1 << Value);

        /// <summary>
        /// 计算反码，即用当前个数的 0 值填充低位， 其余高位填充 1
        /// </summary>
        /// <returns>int 类型反码</returns>
        public int InversedMask() => -1 << Value;

        /// <summary>
        /// 转换比特计数为字节计数，多余比特计数向下舍入
        /// </summary>
        /// <returns>int</returns>
        public int FloorToByteUnit() => Value >> 3;

        /// <summary>
        /// 转换比特计数为字节计数，多余比特计数向上舍入
        /// </summary>
        /// <returns>int</returns>
        public int CeilToByteUnit() => (Value + 7) >> 3;

        /// <summary>
        /// 从字节计数转换为比特计数
        /// </summary>
        /// <param name="a"></param>
        /// <returns></returns>
        public static int BitUnitFromByteUnit(int a) => new BitUnit(a << 3);

        /// <summary>
        /// 计算一个字节内除去本计数后剩余的计数
        /// </summary>
        /// <returns></returns>
        public BitUnit ReverseCount() => new BitUnit(8 - Value);

        /// <summary>
        /// 计算除去整倍数字节长度后的余数
        /// </summary>
        /// <returns></returns>
        public BitUnit InByte() => new BitUnit(Value & 7);
    }
}
